package com.robaone.business;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;

import org.exolab.castor.types.DateTime;

import com.robaone.data.ReportRetriever;
import com.robaone.dbase.types.DateType;
import com.robaone.xml.report.Field;
import com.robaone.xml.report.Header;
import com.robaone.xml.report.Parameter;
import com.robaone.xml.report.Report;
import com.robaone.xml.report.ResultGroup;
import com.robaone.xml.report.types.FieldTypeType;
import com.robaone.xml.report.types.ParameterTypeType;
/**
 * <pre>   Copyright Mar 21, 2012 Ansel Robateau
         http://www.robaone.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.</pre>
 * @author Ansel
 *
 */
public class ReportFactory {
	public static Report getReport(String name,Connection con,ReportRetriever retriever) throws Exception {
		Report rep = retriever.getReport(name, con);
		return rep;
	}
	public static Report createReport(Report reportquery,Connection con) throws Exception {
		Report retval = reportquery;
		String sql = retval.getResultSet().getQuery().getPreparedStatement();
		try{
			PreparedStatement ps = con.prepareStatement(sql);
			ReportFactory.populateStatement(ps,retval.getResultSet().getQuery().getParameter());
			ResultSet rs = ps.executeQuery();
			try{
				com.robaone.xml.report.ResultSet reprs = retval.getResultSet();
				retval.setResultSet(reprs);
				ResultGroup group = new ResultGroup();
				reprs.addResultGroup(group);
				com.robaone.xml.report.Record oldrec = null;
				while(rs.next()){
					com.robaone.xml.report.Record rec = new com.robaone.xml.report.Record();
					
					ResultSetMetaData rsmd = rs.getMetaData();
					int columns = rsmd.getColumnCount();
					for(int i = 1; i <= columns;i++){
						try{
							int type = rsmd.getColumnType(i);
							switch(type){
							case java.sql.Types.BIGINT:
								
							case java.sql.Types.TINYINT:
								
							case java.sql.Types.BINARY:
								
							case java.sql.Types.INTEGER:
								
							case java.sql.Types.BIT:
								int ii = rs.getInt(i);
							com.robaone.xml.report.Field field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.INT);
							field.setContent(""+ii);
							rec.addField(field);
							break;
							case java.sql.Types.BLOB:
								// do something
								break;
							case java.sql.Types.BOOLEAN:
								boolean b = rs.getBoolean(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.INT);
							if(b == true){
								field.setContent("1");
							}else{
								field.setContent("0");
							}
							rec.addField(field);
							break;
							case java.sql.Types.CHAR:
								// do something
								
							case java.sql.Types.CLOB:
								// do something
								
							case java.sql.Types.VARCHAR:
								String str = rs.getString(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.STRING);
							field.setContent(str);
							rec.addField(field);
							break;
							case java.sql.Types.DATE:
							//	java.sql.Date date = rs.getDate(i);
							Timestamp dt = rs.getTimestamp(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.DATE);
							//SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
							//field.setContent(df.format(date));
							DateTime date = new DateTime(dt);
							field.setContent(date.toString());
							
							rec.addField(field);
							break;
							case java.sql.Types.DECIMAL:
								// do something
								break;
							case java.sql.Types.NUMERIC:
								BigDecimal md = rs.getBigDecimal(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.BIGDECIMAL);
							field.setContent(md.toString());
							rec.addField(field);
							break;
							case java.sql.Types.DOUBLE:
								double d = rs.getDouble(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.DOUBLE);
							field.setContent(""+d);
							rec.addField(field);
							break;
							case java.sql.Types.FLOAT:
								float f = rs.getFloat(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.FLOAT);
							field.setContent(""+f);
							rec.addField(field);
							break;
							case java.sql.Types.NULL:
								// do nothing
								break;
							case java.sql.Types.TIME:
								Time t = rs.getTime(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.TIME);
							field.setContent(t.toString());
							rec.addField(field);
							break;
							case java.sql.Types.TIMESTAMP:
								Timestamp ts = rs.getTimestamp(i);
							field = new com.robaone.xml.report.Field();
							field.setName(rsmd.getColumnLabel(i));
							field.setType(FieldTypeType.TIMESTAMP);
							SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a");
							field.setContent(df.format(ts));
							rec.addField(field);
							break;
							}
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					/**
					 * Determine if the current record belongs in this group or in a new
					 * group
					 */
					Header[] headers = retval.getGroup().getHeader();
					boolean add = true;
					for(int i = 0; i < headers.length;i++){
						Header h = headers[i];
						boolean match = false;
						for(int j = 0; j < rec.getFieldCount();j++){
							Field f = rec.getField(j);
							if(h.getField().equalsIgnoreCase(f.getName())){
								if(oldrec == null){
									match = true;
									oldrec = rec;
									break;
								}else if((oldrec.getField(j).getContent() == null && rec.getField(j).getContent() == null) ||
										oldrec.getField(j).getContent().equals(rec.getField(j).getContent())){
									match = true;
									break;
								}else{
									match = false;
									break;
								}
							}
						}
						if(match == false){
							add = false;
							break;
						}
					}
					if(add == false){
						oldrec = rec;
						group = new ResultGroup();
						reprs.addResultGroup(group);
					}
					group.addRecord(rec);
					
				}
			}finally{
				try{ps.close();}catch(Exception e){}
				try{rs.close();}catch(Exception e){}
			}
		}catch(Exception e){
			System.out.println(e.getClass().getName()+": "+e.getMessage());
		}
		return retval;
	}
	
	public static void populateStatement(PreparedStatement ps,
			Parameter[] parameter) {
		int pcount = 1;
		for(int i = 0; i < parameter.length;i++){
			Parameter p = parameter[i];
			int type = p.getType().getType();
			switch(type){
			case ParameterTypeType.BIGDECIMAL_TYPE:
				try{
					BigDecimal bd = new BigDecimal(p.getContent());
					ps.setBigDecimal(pcount, bd);
				}catch(Exception e){}
				break;
			case ParameterTypeType.CHAR_TYPE:
				try{
					char c = p.getContent().charAt(0);
					ps.setString(pcount, ""+c);
				}catch(Exception e){}
				break;
			case ParameterTypeType.DATE_TYPE:
				try{
					SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
					java.util.Date date = df.parse(p.getContent());
					DateType dt = new DateType(date);
					ps.setObject(i+1,dt.getValue());
				}catch(Exception e){}
				break;
			case ParameterTypeType.DOUBLE_TYPE:
				try{
					Double d = new Double(p.getContent());
					ps.setDouble(pcount, d.doubleValue());
				}catch(Exception e){}
				break;
			case ParameterTypeType.FLOAT_TYPE:
				try{
					Float f = new Float(p.getContent());
					ps.setFloat(pcount, f.floatValue());
				}catch(Exception e){}
				break;
			case ParameterTypeType.INT_TYPE:
				try{
					Integer ii = new Integer(p.getContent());
					ps.setInt(pcount, ii.intValue());
				}catch(Exception e){}
				break;
			case ParameterTypeType.LONG_TYPE:
				try{
					Long l = new Long(p.getContent());
					ps.setLong(pcount, l.longValue());
				}catch(Exception e){}
				break;
			case ParameterTypeType.STRING_TYPE:
				try{
					String str = p.getContent();
					ps.setString(pcount, str);
				}catch(Exception e){}
				break;
			case ParameterTypeType.TIME_TYPE:
				try{
					SimpleDateFormat df = new SimpleDateFormat("HH:mm a");
					java.sql.Time t = new java.sql.Time(df.parse(p.getContent()).getTime());
					ps.setTime(pcount, t);
				}catch(Exception e){}
				break;
			case ParameterTypeType.TIMESTAMP_TYPE:
				try{
					SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a");
					java.util.Date date = df.parse(p.getContent());
					ps.setTimestamp(pcount, new Timestamp(date.getTime()));
				}catch(Exception e){}
				break;
			default:
			}
			pcount++;
		}
	}
}
